package com.xht.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xht.constants.SystemConstants;
import com.xht.domain.ResponseResult;
import com.xht.domain.dto.AddArticleDto;
import com.xht.domain.dto.ArticleChangeStatusDto;
import com.xht.domain.entity.Article;
import com.xht.domain.entity.ArticleTag;
import com.xht.domain.entity.Category;
import com.xht.enums.AppHttpCodeEnum;
import com.xht.exception.SystemExceptin;
import com.xht.mapper.ArticleMapper;
import com.xht.service.ArticleService;
import com.xht.service.CategoryService;
import com.xht.utils.BeanCopyUtils;
import com.xht.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ArticleTagServiceImpl articleTagService;
    @Autowired
    private TagServiceImpl tagService;


    /*
    在数据库查询热点文章，按
     */
    @Override
    public ResponseResult hostArticleList() {
        //查询浏览量最高的前10篇

        //创建 装饰器 wrapper
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        //必须是正式文章
        queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
        //按照浏览量进行排序
        queryWrapper.orderByDesc(Article::getViewCount);
        //只显示10篇，可以用分页查询
        Page<Article> page = new Page<>(1, 10);
        page(page, queryWrapper);

        List<Article> articles = page.getRecords();

        //bean 拷贝
        //将Article数据 转为 vo数据 传递给前端
//        List<HotArticleVo> hotArticleVos = new ArrayList<>();
//        for (Article article : articles) {
//            HotArticleVo hotArticleVo = new HotArticleVo();
//            BeanUtils.copyProperties(article,hotArticleVo);
//            hotArticleVos.add(hotArticleVo);
//        }
        List<HotArticleVo> hotArticleVos = BeanCopyUtils.copyBeanList(articles, HotArticleVo.class);

        return ResponseResult.okResult(hotArticleVos);
    }

    @Override
    public ResponseResult articleList(Integer pageNum, Integer pageSize, Long categoryId) {
        /**
         * 分页查询
         * 如果传了 categoryId 就要查询和传入类型相同的文章
         * 状态是正式发布的
         * 置顶的在前面（对IsTop进行降序排序实现）
         */
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
        queryWrapper.orderByDesc(Article::getIsTop)
                .orderByDesc(Article::getCreateTime);
        //若条件(前端传入的categoryId不为空且大于0) 成立，则查询数据库时候会加入判断条件（getCategoryId == categoryId）
        queryWrapper.eq(categoryId != 0 && categoryId > 0, Article::getCategoryId, categoryId);

        //分页查询（记得 在分页那个配置一下）
        Page<Article> page = new Page<>(pageNum, pageSize);
        page(page, queryWrapper);

        List<Article> articles = page.getRecords();
        //第一种循环实现
//        for (Article article : articles) {
//            //查Category表，通过categoryId来获取categoryName
//            Category category = categoryService.getById(article.getCategoryId());
//            article.setCategoryName(category.getName());
//
//        }

        //第二种 利用stream流实现
        articles.stream()
                .map(new Function<Article, Article>() {
                    @Override
                    public Article apply(Article article) {
                        Category category = categoryService.getById(article.getCategoryId());
                        article.setCategoryName(category.getName());

                        //TODO 从redis中获取实时文章浏览量
                        article.setViewCount(GetViewCountFromRedis(article.getId()));

                        return article;
                    }
                }).collect(Collectors.toList());

        //封装查询结果
        List<ArticleListVo> articleListVos = BeanCopyUtils.copyBeanList(page.getRecords(), ArticleListVo.class);
        //前端接收数据 的为 PageVo类型
        PageVo pageVo = new PageVo(articleListVos, page.getTotal());
        return ResponseResult.okResult(pageVo);
    }

    @Override
    public ResponseResult getArticleDetail(Long id) {

        //根据id获取 article
        Article article = getById(id);

        //TODO 从redis中获取文章浏览量
        article.setViewCount(GetViewCountFromRedis(id));

        //再从article中获取 categoryId从而获取 categoryName
        Long categoryId = article.getCategoryId();
        Category category = categoryService.getById(categoryId);
        if (category != null) {
            article.setCategoryName(category.getName());
        }

        //还需要查 article_tag表,找对应该id下的所有数据
        LambdaQueryWrapper<ArticleTag> queryWrapper = Wrappers.lambdaQuery(ArticleTag.class).eq(ArticleTag::getArticleId, id);
        List<String> tags = articleTagService.list(queryWrapper).stream()
                .map(new Function<ArticleTag, String>() {
                    @Override
                    public String apply(ArticleTag articleTag) {
                        Long tagId = articleTag.getTagId();
                        String TagName = tagService.getById(tagId).getName();
                        return TagName;
                    }
                }).collect(Collectors.toList());

        //封装查询结果
        ArticleDetailVo articleDetailVo = BeanCopyUtils.copyBean(article, ArticleDetailVo.class);
        //再将tags数据传入
        articleDetailVo.setTags(tags);
        return ResponseResult.okResult(articleDetailVo);
    }


    /**
     * 更新文章访问量
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult updateViewCount(Long id) {
        //更新redis中文章的浏览量  increase
        stringRedisTemplate.opsForHash().increment(SystemConstants.VIEW_COUNT, id.toString(), SystemConstants.AUTO_INCREASE);

        return ResponseResult.okResult();
    }

    /**
     * 添加  文章
     *
     * @param articleDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)  //任何异常都会让该 事务 回滚
    public ResponseResult add(AddArticleDto articleDto) {
        //异常判断
        //如果文章 标题  分类id 内容 不能为空
        if (!StringUtils.hasText(articleDto.getTitle()) || !StringUtils.hasText(articleDto.getContent()) || articleDto.getCategoryId() == null) {
            throw new SystemExceptin(AppHttpCodeEnum.ARTICLE_NOT_NULL);
        }

        //将文章添加到 文章表
        Article article = BeanCopyUtils.copyBean(articleDto, Article.class);
        save(article);

        List<ArticleTag> articleTags = articleDto.getTags()
                .stream()
                .map(tagId -> new ArticleTag(article.getId(), tagId)
                ).collect(Collectors.toList());

        //将文章和标签的关联信息 添加到 文章-标签表中
        //saveBatch,将多条数据同时 添加进 数据库
        articleTagService.saveBatch(articleTags);

        //同时还需要修改redis中 article:viewcount
        //  先获取新加入的文章的id
//        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(Article::getId, article.getId());
//        Article one = getOne(queryWrapper);
//        Long oneId = one.getId();
//        Long articleId = article.getId();
        Long id = article.getId();
        stringRedisTemplate.opsForHash().put(SystemConstants.VIEW_COUNT, id.toString(), SystemConstants.VIEW_COUNT_NORMAL);

        return ResponseResult.okResult();
    }

    /**
     * 后台管理 展示文章 可以模拟查询和分页
     *
     * @param pageNum  第几页
     * @param pageSize 一页可以显示几条
     * @param title    模拟查询-标题
     * @param summary  模糊查询-文章摘要
     * @return
     */
    @Override
    public ResponseResult AdminList(Integer pageNum, Integer pageSize, String title, String summary, Long categoryId) {

        //模拟查询 + 分页查询 + 被删除的文章不能显示
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        //根据文章标题模糊查询
        if (StringUtils.hasText(title)) {
            queryWrapper.like(Article::getTitle, title);
        }
        //根据文章摘要查询
        if (StringUtils.hasText(summary)) {
            queryWrapper.like(Article::getSummary, summary);
        }
        //根据文章类型查询
        if (categoryId != null) {
            queryWrapper.eq(Article::getCategoryId, categoryId);
        }
        queryWrapper
                .orderByDesc(Article::getStatus)
                .orderByDesc(Article::getCreateTime);
        //queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
        Page<Article> page = new Page<>(pageNum, pageSize);
        page(page, queryWrapper);

        //封装 数据返回前端
        List<Article> articles = page.getRecords();
        List<AdminArticleListVo> adminArticleListVos = BeanCopyUtils.copyBeanList(articles, AdminArticleListVo.class);

        PageVo pageVo = new PageVo(adminArticleListVos, page.getTotal());

        return ResponseResult.okResult(pageVo);
    }

    /**
     * 管理模块-- 文章管理--修改文章时候 根据id 回显 文章
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult fix_ReshowArticle(Integer id) {
        //根据id查询文章
        Article article = getById(id);

        //根据id查询 ArticleTag表中的 tags
        LambdaQueryWrapper<ArticleTag> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ArticleTag::getArticleId, id);
        List<ArticleTag> list = articleTagService.list(queryWrapper);

        List<String> tags = articleTagService.list(queryWrapper).stream()
                .map(new Function<ArticleTag, String>() {
                    @Override
                    public String apply(ArticleTag articleTag) {
                        Long tagId = articleTag.getTagId();
                        return tagId.toString();
                    }
                })
                .collect(Collectors.toList());
        //封转数据返回前端
        AdminUpdateArticleVo adminUpdateArticleVo = BeanCopyUtils.copyBean(article, AdminUpdateArticleVo.class);
        adminUpdateArticleVo.setTags(tags);

        return ResponseResult.okResult(adminUpdateArticleVo);
    }

    /**
     * 更新 文章
     *
     * @param adminUpdateArticleVo
     * @return
     */
    @Override
    @Transactional
    public ResponseResult fix_UpdateArticle(AdminUpdateArticleVo adminUpdateArticleVo) {
        //更新文章表对应的文章
        Article article = BeanCopyUtils.copyBean(adminUpdateArticleVo, Article.class);
        //还需要将CategoryName 写入article
        Category category = categoryService.getById(article.getCategoryId());
        article.setCategoryName(category.getName());

        LambdaUpdateWrapper<Article> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Article::getId, article.getId());
        articleMapper.update(article, updateWrapper);

        //更新ArticleTag表中 的标签 数据
        //先删除所有 包含 该 article_id
        LambdaQueryWrapper<ArticleTag> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ArticleTag::getArticleId, article.getId());
        articleTagService.getBaseMapper().delete(queryWrapper);
        //再重新更新数据
        List<ArticleTag> articleTags = new ArrayList<>();
        List<String> tags = adminUpdateArticleVo.getTags();
        tags.forEach(tagId -> {
            ArticleTag articleTag = new ArticleTag(article.getId(), Long.parseLong(tagId));
            articleTags.add(articleTag);
        });
        //再保存到 数据库中
        articleTagService.saveBatch(articleTags);

        return ResponseResult.okResult();
    }

    /**
     * 根据 文章id 逻辑删除 文章
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult deleteArticle(Integer id) {

        LambdaUpdateWrapper<Article> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Article::getId, id)
                .set(Article::getDelFlag, SystemConstants.ARTICLE_STATUS_DELETE);
        update(updateWrapper);

        return ResponseResult.okResult();
    }

    /**
     * 修改文章 发布状态
     *
     * @param articleChangeStatusDto
     * @return
     */
    @Override
    public ResponseResult changeStatus(ArticleChangeStatusDto articleChangeStatusDto) {

        Long articleId = articleChangeStatusDto.getArticleId();
        LambdaUpdateWrapper<Article> wrapper = Wrappers.lambdaUpdate(Article.class).eq(Article::getId, articleId)
                .set(Article::getStatus, articleChangeStatusDto.getStatus());
        update(wrapper);
        return ResponseResult.okResult();
    }


    // 文章浏览量 从redis取出 展示到前端 的数据
    public Long GetViewCountFromRedis(Long id) {
        //TODO 从redis中获取文章浏览量
        Object o = stringRedisTemplate.opsForHash().get(SystemConstants.VIEW_COUNT, id.toString());
        Long viewCount = Long.parseLong(o.toString());
        return viewCount;
    }
}
